#include "CustomizedThreads.h"

#include <sys/time.h>

LinuxEvent::LinuxEvent(bool bIsManualReset, bool bInitialSignaled)
: m_bIsManualReset(bIsManualReset)
, m_bEventStatus(bInitialSignaled)
, m_bMutexInitialized(false)
, m_bCondInitialized(false)
{

}

LinuxEvent::~LinuxEvent()
{
    if (m_bMutexInitialized)
    {
        pthread_mutex_destroy(&m_mutex);
        m_bMutexInitialized = false;
    }

    if (m_bCondInitialized)
    {
        pthread_cond_destroy(&m_cond);
        m_bCondInitialized = false;
    }
}

bool LinuxEvent::CreateEvent()
{
    if (!m_bMutexInitialized)
    {
        if (0 == pthread_mutex_init(&m_mutex, NULL))
        {
            m_bMutexInitialized = true;
        }
    }

    if (!m_bCondInitialized)
    {
        if (0 == pthread_cond_init(&m_cond, NULL))
        {
            m_bCondInitialized = true;
        }
    }

    return (m_bMutexInitialized && m_bCondInitialized);
}

bool LinuxEvent::IsInitialized()
{
    return (m_bMutexInitialized && m_bCondInitialized);
}

bool LinuxEvent::Set()
{
    if (!IsInitialized())
    {
        return false;
    }

    pthread_mutex_lock(&m_mutex);
    m_bEventStatus = true;
    pthread_cond_broadcast(&m_cond);
    pthread_mutex_unlock(&m_mutex);

    return true;
}

bool LinuxEvent::Reset()
{
    if (!IsInitialized())
    {
        return false;
    }

    pthread_mutex_lock(&m_mutex);
    m_bEventStatus = false;
    pthread_mutex_unlock(&m_mutex);

    return true;
}

bool LinuxEvent::Wait(int t_ms)
{
    if (!IsInitialized())
    {
        return false;
    }

    pthread_mutex_lock(&m_mutex);
    int error = 0;

    if (t_ms != INFINITE)
    {
        struct timeval tv;
        gettimeofday(&tv, NULL);

        struct timespec ts;
        ts.tv_sec = tv.tv_sec + (t_ms / 1000);
        ts.tv_nsec = tv.tv_usec * 1000 + (t_ms % 1000) * 1000000;

        while (ts.tv_nsec >= 1000000000)
        {
            ts.tv_sec++;
            ts.tv_nsec -= 1000000000;
        }

        while ((!m_bEventStatus) && (error == 0))
        {
            error = pthread_cond_timedwait(&m_cond, &m_mutex, &ts);
        }
    }
    else
    {
        while ((!m_bEventStatus) && (error == 0))
        {
            error = pthread_cond_wait(&m_cond, &m_mutex);
        }
    }

    if (0 == error && !m_bIsManualReset)
    {
        m_bEventStatus = false;
    }

    pthread_mutex_unlock(&m_mutex);

    return (0 == error);
}

LinuxEvent *CreateEvent(bool bManualReset, bool bInitialState)
{
    LinuxEvent *pEvent = new LinuxEvent(bManualReset, bInitialState);
    assert(pEvent);

    bool bRet = pEvent->CreateEvent();
    assert(bRet);

    return pEvent;
}

unsigned int WaitForSingleObject(LinuxEvent *pEvent, int t_ms)
{
    assert(pEvent);

    if(pEvent->Wait(t_ms))
    {
        return WAIT_OBJECT_0;
    }

    return WAIT_TIMEOUT;
}

unsigned int WaitForSingleObject(pthread_mutex_t mutex, int t_ms)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);

    struct timespec ts;
    ts.tv_sec = tv.tv_sec + (t_ms / 1000);
    ts.tv_nsec = tv.tv_usec * 1000 + (t_ms % 1000) * 1000000;

    while (ts.tv_nsec >= 1000000000)
    {
        ts.tv_sec++;
        ts.tv_nsec -= 1000000000;
    }

    return pthread_mutex_timedlock(&mutex, &ts);
}


void TimedEvent::CreateTimedEvent(unsigned char bOn, unsigned long timeInt)
{
	m_Event = CreateEvent(false, bOn ? true : false);
	m_timeInt = timeInt;
	m_bPrevSet = 0;
}

bool SetEvent(TimedEvent& te)
{
	if (!te.m_bPrevSet)
	{
		te.m_prevTick = high_resolution_clock::now();
		te.m_bPrevSet = 1;
		return SetEvent(te.m_Event);
	}
	else
	{
		system_clock::time_point tp = high_resolution_clock::now();
		double elapsed = duration_cast<nanoseconds>
			(tp - te.m_prevTick).count() / (1e6);
		if (elapsed >= te.m_timeInt)
		{
			te.m_prevTick = tp;
			te.m_bPrevSet = 1;
			return SetEvent(te.m_Event);
		}
		else
			return false;
	}
}

bool CloseTimedEvent(TimedEvent& te)
{
	if (te.m_Event)
	{
		if (!CloseHandle(te.m_Event))
			return false;
		te.m_Event = NULL;
		return true;
	}
	return true;
}

ATOM_Event_List::ATOM_Event_List()
{
	m_nHandleCount = 0;
	m_pHandleArray = 0;
	m_pbTimedHandle = 0;
}
#include <iostream>
using namespace std;
bool ATOM_Event_List::Initialize(int nHandle,
	BYTE* pTimedHandle, unsigned long *pWaitTimeInMS)
{
	if ((nHandle > MAX_NUM_EVENTS_IN_A_LIST) || (nHandle < 0))
		return false;
	m_pHandleArray = new void*[nHandle];
	if (!m_pHandleArray)
    {
		return false;
    }
	m_pbTimedHandle = new BYTE[nHandle];
	if (!m_pbTimedHandle)
	{
		delete [] m_pHandleArray;
		return false;
	}
	m_nHandleCount = nHandle;
	for (int i = 0; i < nHandle; ++i)
	{
		if (*(pTimedHandle + i) != 0 && *(pTimedHandle + i) != 1)
		{
			delete[] m_pHandleArray;
			delete[] m_pbTimedHandle;
			m_nHandleCount = 0;
			return false;
		}
		m_pbTimedHandle[i] = pTimedHandle[i];
	}

	for (int i = 0; i < nHandle; ++i)
	{
		unsigned long wtime;
		if (!pWaitTimeInMS)
			wtime = 2000;
		else
			wtime = (pWaitTimeInMS[i] > 5000) ? 5000 : pWaitTimeInMS[i];
		if (m_pbTimedHandle[i])
			m_pHandleArray[i] = new TimedEvent(false, wtime);
		else
			m_pHandleArray[i] = CreateEvent(false, false);

		if (!m_pHandleArray[i])
		{
			for (int j = 0; j < i; ++j)
                CloseHandle((LinuxEvent*)m_pHandleArray[j]);

			delete[] m_pHandleArray;
			delete[] m_pbTimedHandle;
			m_nHandleCount = 0;
			return false;
		}
	}

	return true;
}

void ATOM_Event_List::CloseAll()
{
	for (int i = 0; i < m_nHandleCount; ++i)
	{
		if (m_pHandleArray[i])
			if (m_pbTimedHandle[i])
				CloseTimedEvent(*((TimedEvent*)m_pHandleArray[i]));
			else
                CloseHandle((LinuxEvent*)m_pHandleArray[i]);
	}

	if (m_nHandleCount > 0)
	{
		delete[] m_pHandleArray;
		m_pHandleArray = 0;
		delete[] m_pbTimedHandle;
		m_pbTimedHandle = 0;
		m_nHandleCount = 0;
	}
}

bool ATOM_Event_List::ReInstate()
{
	bool ret = true;
	for (int i = 0; i < m_nHandleCount; ++i)
	{
		if (m_pHandleArray[i])
			if (m_pbTimedHandle[i])
			{
				((TimedEvent*)m_pHandleArray[i])->ClearPrevTick();
				ret = ret && ResetEvent(*((TimedEvent*)m_pHandleArray[i]));
			}
			else
				ret = ret && ResetEvent((LinuxEvent*)m_pHandleArray[i]);
	}
	return ret;
}

bool ATOM_Event_List::Wait(int nEventNum, unsigned long dwWaitTimeInMS)
{
	if (nEventNum >= m_nHandleCount)
		return false;
	if (m_pbTimedHandle[nEventNum])
		return (WaitForSingleObject(*((TimedEvent*)(m_pHandleArray[nEventNum])),
		dwWaitTimeInMS) == WAIT_OBJECT_0);
	else
		return (WaitForSingleObject((LinuxEvent*)(m_pHandleArray[nEventNum]),dwWaitTimeInMS) == WAIT_OBJECT_0);
}

bool ATOM_Event_List::Set(int nEventNum)
{
	if (nEventNum >= m_nHandleCount)
		return false;
	if (m_pbTimedHandle[nEventNum])
		return SetEvent(*((TimedEvent*)(m_pHandleArray[nEventNum])));
	else
		return SetEvent((LinuxEvent*)(m_pHandleArray[nEventNum]));
}
